package com.icesoft.system.controller;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.IdUtil;
import com.icesoft.framework.core.cache.RedisCacheManager;
import com.icesoft.framework.core.config.Cons;
import com.icesoft.framework.core.controller.BaseController;
import com.icesoft.framework.core.exception.BusinessException;
import com.icesoft.framework.core.vo.JsonResult;
import com.icesoft.system.annotation.SafeRequest;
import com.icesoft.system.auth.credential.EmailCredential;
import com.icesoft.system.auth.credential.PhoneCredential;
import com.icesoft.system.auth.credential.PwdCredential;
import com.icesoft.system.auth.service.LoginService;
import com.icesoft.system.handler.LoginHandler;
import com.icesoft.system.safe.model.CryptoKeyPair;
import com.icesoft.system.safe.service.CryptoKeyPairRepository;
import com.icesoft.system.sender.email.EmailSender;
import com.icesoft.system.sender.sms.SmsSender;
import com.icesoft.system.types.LoginSettingDictionary;
import com.icesoft.system.util.VerifyCodeUtils;
import com.icesoft.system.vo.UserVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/auth")
public class AuthController extends BaseController {

	private final CryptoKeyPairRepository cryptoKeyPairRepository;

	private final LoginSettingDictionary loginSettingDictionary;

	private final RedisCacheManager redisCacheManager;

	private final LoginService loginService;

	private final List<LoginHandler> loginHandlerList;

	private final EmailSender emailSender;

	private final SmsSender smsSender;

	@RequestMapping("/getPublicKey")
	public JsonResult<?> getPublicKey() {
		String key = "";
		Map<String, CryptoKeyPair> stringCryptoKeyPairMap = cryptoKeyPairRepository.find();
		for (String s : stringCryptoKeyPairMap.keySet()) {
			key = s;
		}
		CryptoKeyPair cryptoKeyPair = stringCryptoKeyPairMap.get(key);
		cryptoKeyPair.setPrivateKey(null);
		return JsonResult.OK(cryptoKeyPair);
	}

	@GetMapping("/loginSetting")
	public JsonResult<?> loginSetting() {
		return JsonResult.OK(loginSettingDictionary);
	}

	@SafeRequest
	@PostMapping("/login")
	public JsonResult<?> login(HttpServletRequest request, @RequestBody PwdCredential pwdCredential) {
		UserVO user = null;
		try {
			user = loginService.pwdCredentialLogin(pwdCredential);
			if (loginHandlerList != null) {
				UserVO finalUser = user;
				loginHandlerList.forEach(loginHandler -> loginHandler.success(request, "账号登录", finalUser));
			}
			return JsonResult.OK(user);
		} catch (Exception e) {
			if (loginHandlerList != null && user != null) {
				UserVO finalUser1 = user;
				loginHandlerList.forEach(loginHandler -> loginHandler.error(request, "账号登录", finalUser1, e));
			}
			return JsonResult.FAIL_EXCEPTION(e.getMessage());
		}
	}

	@SafeRequest
	@PostMapping("/loginByPhone")
	public JsonResult<?> loginByPhone(HttpServletRequest request, @RequestBody PhoneCredential phoneCredential) {
		UserVO user = null;
		try {
			user = loginService.phoneCredentialLogin(phoneCredential);
			String verifyKey = Cons.PHONE_CODE_KEY + phoneCredential.getPhone();
			String code = redisCacheManager.getCacheObject(verifyKey);
			if (!phoneCredential.getCode().equalsIgnoreCase(code)) {
				throw new BusinessException("短信验证码不正确");
			}
			redisCacheManager.deleteObject(verifyKey);

			if (loginHandlerList != null) {
				UserVO finalUser = user;
				loginHandlerList.forEach(loginHandler -> loginHandler.success(request, "手机号登录", finalUser));
			}
			return JsonResult.OK(user);
		} catch (Exception e) {
			if (loginHandlerList != null) {
				UserVO finalUser1 = user;
				loginHandlerList.forEach(loginHandler -> loginHandler.error(request, "手机号登录", finalUser1, e));
			}
			return JsonResult.FAIL_EXCEPTION(e.getMessage());
		}
	}

	@SafeRequest
	@PostMapping("/loginByEmail")
	public JsonResult<?> emailCredentialLogin(HttpServletRequest request, @RequestBody EmailCredential emailCredential) {
		UserVO user = null;
		try {
			user = loginService.emailCredentialLogin(emailCredential);
			String verifyKey = Cons.EMAIL_CODE_KEY + emailCredential.getEmail();
			String code = redisCacheManager.getCacheObject(verifyKey);
			if (!emailCredential.getCode().equalsIgnoreCase(code)) {
				throw new BusinessException("验证码不正确");
			}
			redisCacheManager.deleteObject(verifyKey);
			if (loginHandlerList != null) {
				UserVO finalUser = user;
				loginHandlerList.forEach(loginHandler -> loginHandler.success(request, "邮箱登录", finalUser));
			}
			return JsonResult.OK(user);
		} catch (Exception e) {
			if (loginHandlerList != null) {
				UserVO finalUser1 = user;
				loginHandlerList.forEach(loginHandler -> loginHandler.error(request, "邮箱登录", finalUser1, e));
			}
			return JsonResult.FAIL_EXCEPTION(e.getMessage());
		}
	}

	@PostMapping("/logout")
	public JsonResult<?> logout() {
		loginService.logout();
		return JsonResult.OK("成功退出");
	}

	@GetMapping("/captchaImage")
	public JsonResult<?> captchaImage() throws IOException {
		// 生成随机字串
		String verifyCode = VerifyCodeUtils.generateVerifyCode(4);
		// 唯一标识
		String uuid = IdUtil.simpleUUID();
		String verifyKey = Cons.CAPTCHA_CODE_KEY + uuid;

		redisCacheManager.setCacheObject(verifyKey, verifyCode, Long.parseLong(String.valueOf(Cons.CAPTCHA_EXPIRATION)), TimeUnit.MINUTES);
		// 生成图片
		int w = 159, h = 56;
		ByteArrayOutputStream stream = new ByteArrayOutputStream();
		VerifyCodeUtils.outputImage(w, h, stream, verifyCode);
		try {
			Map<String, Object> map = new LinkedHashMap<>();
			map.put("uuid", uuid);
			map.put("img", Base64.encode(stream.toByteArray()));
			return JsonResult.OK(map);
		} catch (Exception e) {
			log.error("获取验证码异常", e);
			return JsonResult.FAIL_AUTHENTICATION(e.getMessage());
		} finally {
			stream.close();
		}
	}

	@GetMapping("/sendMail")
	public JsonResult<?> sendMail(String email) throws Exception {
		String verifyCode = VerifyCodeUtils.generateVerifyCode(4);
		String verifyKey = Cons.EMAIL_CODE_KEY + email;
		redisCacheManager.setCacheObject(verifyKey, verifyCode, Long.parseLong(String.valueOf(Cons.CAPTCHA_EXPIRATION)), TimeUnit.MINUTES);
		emailSender.sendEmailCode(email, verifyCode);
		return JsonResult.OK();
	}

	@GetMapping("/sendSmsCode")
	public JsonResult<?> sendSmsCode(String phone) throws Exception {
		Random random = new Random();
		String verifyCode = String.valueOf(random.nextInt(900000) + 100000);
		String verifyKey = Cons.PHONE_CODE_KEY + phone;
		redisCacheManager.setCacheObject(verifyKey, verifyCode, Long.parseLong(String.valueOf(Cons.CAPTCHA_EXPIRATION)), TimeUnit.MINUTES);
		smsSender.sendSmsCode(phone, verifyCode);
		return JsonResult.OK();
	}
}
